package cool.houge.pig.infra.service.webhook;

import com.google.common.annotations.VisibleForTesting;
import cool.houge.pig.PigProps;
import cool.houge.pig.model.User;
import cool.houge.pig.service.webhook.SharedWebhookService;
import cool.houge.pig.service.webhook.WebhookException;
import cool.houge.pig.service.webhook.WebhookInput;
import cool.houge.pig.service.webhook.WebhookStatusException;
import cool.houge.pig.service.webhook.WebhookType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * Web 回调的勾子.
 *
 * @author KK (kzou227@qq.com)
 */
@Service
public class WebhookServiceImpl implements SharedWebhookService {

  private static final Logger log = LogManager.getLogger();
  private final PigProps pigProps;
  private final WebClient webClient;

  /**
   * @param pigProps
   * @param webClient
   */
  public WebhookServiceImpl(PigProps pigProps, WebClient webClient) {
    this.pigProps = pigProps;
    this.webClient = webClient;
  }

  @Override
  public Mono<Void> invoke(WebhookInput input) {
    return Flux.fromIterable(pigProps.getWebhooks())
        .filter(item -> item.getTypePattern().matcher(input.getType().label).matches())
        .flatMap(item -> launchRequest(item, input))
        .then();
  }

  @Override
  public Mono<Void> invokeRegister(User user) {
    // FIXME WEBHOOK 请求数据需要加密编码
    var metadata = WebhookInput.Metadata.builder().build();
    var body = WebhookInput.Body.builder().uid(user.getId()).build();
    var input =
        WebhookInput.builder().type(WebhookType.REGISTER).metadata(metadata).body(body).build();
    return this.invoke(input);
  }

  @VisibleForTesting
  Mono<Void> launchRequest(PigProps.Webhook item, WebhookInput input) {
    log.debug("Web回调请求初始化 type={} url={}", input.getType(), item.getUrl());
    return webClient
        .post()
        .uri(item.getUrl())
        .bodyValue(input)
        .exchangeToMono(
            response -> {
              if (response.rawStatusCode() != item.getSuccessStatus()) {
                // 忽略回调错误
                if (item.isIgnoreFailed()) {
                  log.info(
                      "【已忽略】Web回调请求响应状态不符合 url={} type={} httpStatus={}",
                      item.getUrl(),
                      input.getType(),
                      response.rawStatusCode());
                  return Mono.empty();
                }
                return Mono.error(
                    new WebhookStatusException(
                        item.getUrl(), item.getSuccessStatus(), response.rawStatusCode()));
              }
              log.debug("Web回调请求成功 url={} type={}", item.getUrl(), input.getType());
              return Mono.<Void>empty();
            })
        .onErrorResume(
            t -> !(t instanceof WebhookException),
            t -> {
              if (item.isIgnoreFailed()) {
                log.info("【已忽略】Web回调请求请求异常", t);
                return Mono.empty();
              }
              return Mono.error(new WebhookException(item.getUrl(), t));
            });
  }
}
